package com.sh.data.engine.domain.datadev.offline.service.impl;

import cn.hutool.core.convert.Convert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.sh.data.engine.common.util.ServletUtils;
import com.sh.data.engine.domain.base.model.PageResult;
import com.sh.data.engine.domain.datadev.offline.model.domain.OfflineDevOperationTaskManageDomain;
import com.sh.data.engine.domain.datadev.offline.model.domain.OfflineDevTaskFlowVNodeDetailDomain;
import com.sh.data.engine.domain.datadev.offline.model.enums.OfflineDevNodeTypeEnum;
import com.sh.data.engine.domain.datadev.offline.model.param.OfflineDevTaskManagerSearchParam;
import com.sh.data.engine.domain.datadev.offline.service.*;
import com.sh.data.engine.domain.workspace.manager.model.domain.UserDomain;
import com.sh.data.engine.domain.workspace.manager.service.UserService;
import com.sh.data.engine.repository.dao.datadev.DatadevMenuNodeMapper;
import com.sh.data.engine.repository.dao.datadev.entity.DatadevMenuNodeEntity;
import com.sh.data.engine.repository.dao.datadev.entity.DatadevSubNodeDependencyEntity;
import com.sh.data.engine.repository.dao.datadev.entity.DatadevVnodeEntity;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class DatadevMenuNodeServiceImpl
    extends ServiceImpl<DatadevMenuNodeMapper, DatadevMenuNodeEntity>
    implements DatadevMenuNodeService {

    @Autowired
    private DatadevVnodeSerivce datadevVnodeSerivce;

    @Autowired
    private UserService userService;

    @Lazy
    @Autowired
    private OfflineDevMenuNodeService offlineDevMenuNodeService;

    @Autowired
    private DatadevJobService datadevJobService;

    @Autowired
    private DatadevSubNodeService datadevSubNodeService;

    @Autowired
    private DatadevSubNodeDependencyService datadevSubNodeDependencyService;

    @Override
    public boolean hasSameNode(Long excludeId, Long pid, String name, Long projectId) {

        LambdaQueryWrapper<DatadevMenuNodeEntity> lambdaQueryWrapper =
            new LambdaQueryWrapper<DatadevMenuNodeEntity>()
                .eq(DatadevMenuNodeEntity::getPid, pid)
                .eq(DatadevMenuNodeEntity::getProjectId, projectId)
                .eq(DatadevMenuNodeEntity::getName, name)
                .eq(DatadevMenuNodeEntity::getRowState, 1);
        if (null != excludeId) {
            lambdaQueryWrapper.ne(DatadevMenuNodeEntity::getId, excludeId);
        }
        long count = count(lambdaQueryWrapper);
        if (count > 0) {
            return true;
        }

        return false;
    }

    @Override
    public Integer getMaxSortByPid(Long pid) {

        List<DatadevMenuNodeEntity> list =
            list(
                new LambdaQueryWrapper<DatadevMenuNodeEntity>()
                    .eq(DatadevMenuNodeEntity::getPid, pid)
                    .eq(DatadevMenuNodeEntity::getRowState, 1));
        if (CollectionUtils.isNotEmpty(list)) {
            return list.get(0).getSort();
        }

        return 0;
    }

    @Override
    public DatadevMenuNodeEntity getDatadevMenuNodeByFlowId(
        Long flowId, OfflineDevNodeTypeEnum offlineDevNodeTypeEnum) {

        LambdaQueryWrapper<DatadevMenuNodeEntity> eq =
            new LambdaQueryWrapper<DatadevMenuNodeEntity>()
                .eq(DatadevMenuNodeEntity::getPid, flowId)
                .eq(DatadevMenuNodeEntity::getNodeType, offlineDevNodeTypeEnum.getNodeType());
        DatadevMenuNodeEntity one = getOne(eq, false);
        return one;
    }

    @Override
    public List<DatadevMenuNodeEntity> getDatadevMenuNodeByFlowIds(
        List<Long> flowIds, OfflineDevNodeTypeEnum offlineDevNodeTypeEnum) {
        LambdaQueryWrapper<DatadevMenuNodeEntity> eq =
            new LambdaQueryWrapper<DatadevMenuNodeEntity>()
                .in(DatadevMenuNodeEntity::getPid, flowIds)
                .eq(DatadevMenuNodeEntity::getRowState, 1)
                .eq(DatadevMenuNodeEntity::getNodeType, offlineDevNodeTypeEnum.getNodeType())
                .orderByAsc(DatadevMenuNodeEntity::getId);
        List<DatadevMenuNodeEntity> list = this.list(eq);
        return list;
    }

    @Override
    public List<DatadevMenuNodeEntity> getAllTaskFlowByProjectId(Long projectId) {
        LambdaQueryWrapper<DatadevMenuNodeEntity> listLam =
            new LambdaQueryWrapper<DatadevMenuNodeEntity>()
                .eq(DatadevMenuNodeEntity::getProjectId, projectId)
                .eq(DatadevMenuNodeEntity::getRowState, 1)
                .eq(DatadevMenuNodeEntity::getNodeType,
                    OfflineDevNodeTypeEnum.DIR_TASK_FLOW.getNodeType())
                .isNotNull(DatadevMenuNodeEntity::getIsLocked);
        List<DatadevMenuNodeEntity> list = list(listLam);
        return list;
    }

    private String getScheduleInfo(OfflineDevTaskFlowVNodeDetailDomain schedule) {
        if (schedule == null) {
            return null;
        }
        StringBuilder sb = new StringBuilder("执行时间: ");
        Integer startTimeHour = schedule.getStartTimeHour();
        Integer startTimeMin = schedule.getStartTimeMin();
        Integer endTimeHour = schedule.getEndTimeHour();
        Integer endTimeMin = schedule.getEndTimeMin();
        String startTimeHourValue = String.valueOf(startTimeHour);
        String startTimeMinValue = String.valueOf(startTimeMin);
        String endTimeHourValue = String.valueOf(endTimeHour);
        String endTimeMinValue = String.valueOf(endTimeMin);

        if (null != startTimeHour && startTimeHour < 10) {
            startTimeHourValue = 0 + startTimeHourValue;
        }
        if (null != startTimeMin && startTimeMin < 10) {
            startTimeMinValue = 0 + startTimeMinValue;
        }
        if (null != endTimeHour && endTimeHour < 10) {
            endTimeHourValue = 0 + endTimeHourValue;
        }
        if (null != endTimeMin && endTimeMin < 10) {
            endTimeMinValue = 0 + endTimeMinValue;
        }
        sb.append(startTimeHourValue);
        sb.append(":");
        sb.append(startTimeMinValue);
        Integer cycle = schedule.getCycle();
        if (cycle == null) {
            return null;
        }
        if (cycle == 0) {
            sb.append("\n执行间隔: ");
            sb.append("每月");
            sb.append(schedule.getExecPoint());
            sb.append("号");
        } else if (cycle == 1) {
            sb.append("\n执行间隔: ");
            sb.append("每周");
            sb.append(schedule.getExecPoint());
        } else if (cycle == 2) {

        } else if (cycle == 3) {
            sb.append("~");
            sb.append(endTimeHourValue);
            sb.append(":");
            sb.append(endTimeMinValue);
            sb.append("\n执行间隔: ");
            sb.append("每");
            sb.append(schedule.getExecPoint());
            sb.append("小时");
        } else if (cycle == 4) {
            sb.append("~");
            sb.append(endTimeHourValue);
            sb.append(":");
            sb.append(endTimeMinValue);
            sb.append("\n执行间隔: ");
            sb.append("每");
            sb.append(schedule.getExecPoint());
            sb.append("分钟");
        }
        return sb.toString();
    }

    @Override
    public PageResult<OfflineDevOperationTaskManageDomain> getOfflineDevTaskManageList(
        OfflineDevTaskManagerSearchParam param) {
        Long projectId = ServletUtils.getProjectId();
        LambdaQueryWrapper<DatadevMenuNodeEntity> lambdaQueryWrapper =
            new LambdaQueryWrapper<DatadevMenuNodeEntity>()
                .eq(DatadevMenuNodeEntity::getProjectId, projectId)
                .eq(DatadevMenuNodeEntity::getRowState, 1);

        if (StringUtils.isNotBlank(param.getNodeName())) {
            lambdaQueryWrapper.like(DatadevMenuNodeEntity::getName, param.getNodeName());
        }

        lambdaQueryWrapper.eq(DatadevMenuNodeEntity::getRowState, 1);
        lambdaQueryWrapper.eq(
            Objects.nonNull(param.getUserId()), DatadevMenuNodeEntity::getCreatorId, param.getUserId());

        // 过滤调度周期

        if (StringUtils.isNotBlank(param.getCycle())) {
            List<DatadevMenuNodeEntity> allTaskFlowByProjectId =
                this.getAllTaskFlowByProjectId(projectId);
            List<Integer> cycles =
                Arrays.stream(param.getCycle().split(","))
                    .map(e -> Integer.valueOf(e))
                    .collect(Collectors.toList());
            List<Long> collect =
                allTaskFlowByProjectId.stream().map(e -> e.getId()).collect(Collectors.toList());
            List<Long> flowIds = datadevVnodeSerivce.getDatadevVnodeByFlowIds(collect, cycles);
            if (CollectionUtils.isNotEmpty(flowIds)) {
                lambdaQueryWrapper.in(
                    CollectionUtils.isNotEmpty(flowIds), DatadevMenuNodeEntity::getId, flowIds);
            } else {
                return new PageResult<>();
            }
        }
        // 是否冻结
        if (StringUtils.isNotBlank(param.getIsLocked())) {
            List<Integer> lockedList =
                Arrays.stream(param.getIsLocked().split(","))
                    .map(e -> Integer.valueOf(e))
                    .collect(Collectors.toList());
            lambdaQueryWrapper.in(DatadevMenuNodeEntity::getIsLocked, lockedList);
        }
        // 是否发布
        if (StringUtils.isNotBlank(param.getPublishStatus())) {
            List<Integer> publishStatus =
                Arrays.stream(param.getPublishStatus().split(","))
                    .map(e -> Integer.valueOf(e))
                    .collect(Collectors.toList());
            if (publishStatus.contains(1) && !publishStatus.contains(0)) {
                lambdaQueryWrapper.isNotNull(DatadevMenuNodeEntity::getXxlJobId);
            } else if (!publishStatus.contains(1) && publishStatus.contains(0)) {
                lambdaQueryWrapper.isNull(DatadevMenuNodeEntity::getXxlJobId);
            }
        }

        if (StringUtils.isNotBlank(param.getNodeType())) {
            List<Integer> nodeTypes =
                Arrays.stream(param.getNodeType().split(","))
                    .map(e -> Integer.valueOf(e))
                    .collect(Collectors.toList());

            List<DatadevMenuNodeEntity> allTaskFlowByProjectId =
                this.getAllTaskFlowByProjectId(projectId);

            List<Long> flowIds = Lists.newArrayList();

            // 过滤节点
            if (nodeTypes.contains(OfflineDevNodeTypeEnum.DIR_TASK_FLOW.getNodeType())
                && nodeTypes.size() == 1) {
            } else {
                for (DatadevMenuNodeEntity datadevMenuNodeEntity : allTaskFlowByProjectId) {
                    Long id = datadevMenuNodeEntity.getId();
                    boolean b = datadevSubNodeService.checkTaskFlowContainTaskType(id, nodeTypes);
                    if (!b) {
                        flowIds.add(id);
                    }
                }
                if (CollectionUtils.isNotEmpty(flowIds)) {
                    lambdaQueryWrapper.notIn(DatadevMenuNodeEntity::getId, flowIds);
                }
            }
        }
        lambdaQueryWrapper.eq(
            DatadevMenuNodeEntity::getNodeType, OfflineDevNodeTypeEnum.DIR_TASK_FLOW.getNodeType());
        String orderField = param.getOrderField();
        if (Objects.nonNull(orderField)) {
            if (orderField.equals("publishTime")) {
                lambdaQueryWrapper.last("order by " + " publish_time " + param.getOrder());
            }
        } else {
            lambdaQueryWrapper.orderByDesc(DatadevMenuNodeEntity::getCreateTime);
        }
        IPage<OfflineDevOperationTaskManageDomain> r2 =
            this.page(new Page<>(param.getPageNum(), param.getPageSize()), lambdaQueryWrapper)
                .convert(e -> Convert.convert(OfflineDevOperationTaskManageDomain.class, e));

        List<OfflineDevOperationTaskManageDomain> records = r2.getRecords();

        List<String> userIds = records.stream().map(e -> e.getCreatorId()).collect(Collectors.toList());

        Map<String, String> mapByUserIds = userService.getMapByUserIds(userIds);
        // 组装字段
        records.forEach(
            e -> {
                e.setPublishStatus(1);
                if (Objects.isNull(e.getXxlJobId())) {
                    e.setPublishStatus(0);
                }
                e.setCanReRun(1);
                String username = mapByUserIds.get(e.getCreatorId());
                e.setUserName(username);
                OfflineDevTaskFlowVNodeDetailDomain nodeDetailByTaskFlowId =
                    offlineDevMenuNodeService.getNodeDetailByTaskFlowId(e.getId(), projectId);
                if (Objects.nonNull(nodeDetailByTaskFlowId)) {
                    String schedule = this.getScheduleInfo(nodeDetailByTaskFlowId);
                    e.setCycle(nodeDetailByTaskFlowId.getCycle());
                    e.setScheduleInfo(schedule);
                }
                e.setHaveChildren(1);
                e.setRunType(0);
                // 判读是否运行过
                boolean b = datadevJobService.haveRecordByNodeId(e.getId());
                if (b) {
                    e.setRunType(1);
                }
            });
        return PageResult.convert(r2);
    }

    @Override
    public List<OfflineDevOperationTaskManageDomain> getOfflineDevTaskManageChildren(
        Long nodeId, Long projectId, Integer nodeType) {

        List<DatadevSubNodeDependencyEntity> datadevSubNodeDepenDencyList =
            datadevSubNodeDependencyService.getDatadevSubNodeDepenDencyList(nodeId);
        DatadevMenuNodeEntity flow = getById(nodeId);
        Set<Long> nodeIds = Sets.newHashSet();
        datadevSubNodeDepenDencyList.forEach(
            e -> {
                nodeIds.add(e.getUpNodeId());
                nodeIds.add(e.getDownNodeId());
            });
        List<DatadevMenuNodeEntity> nodeEntityList = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(nodeIds)) {
            nodeEntityList = this.listByIds(nodeIds);
        }

        // 如果为空，放置开始节点
        if (CollectionUtils.isEmpty(nodeEntityList)) {
            DatadevVnodeEntity datadevVnodeByFlowId = datadevVnodeSerivce.getDatadevVnodeByFlowId(nodeId);
            DatadevMenuNodeEntity byId = this.getById(datadevVnodeByFlowId.getNodeId());
            nodeEntityList.add(byId);
        }

        List<OfflineDevOperationTaskManageDomain> domainList = Lists.newArrayList();
        for (DatadevMenuNodeEntity datadevMenuNodeEntity : nodeEntityList) {
            OfflineDevOperationTaskManageDomain convert =
                Convert.convert(OfflineDevOperationTaskManageDomain.class, datadevMenuNodeEntity);
            convert.setNodeId(datadevMenuNodeEntity.getId());
            domainList.add(convert);
        }
        OfflineDevTaskFlowVNodeDetailDomain nodeDetailByTaskFlowId =
            offlineDevMenuNodeService.getNodeDetailByTaskFlowId(flow.getId(), projectId);
        String schedule = this.getScheduleInfo(nodeDetailByTaskFlowId);
        UserDomain user = userService.getUserById(flow.getCreatorId());
        ListIterator<OfflineDevOperationTaskManageDomain> iterator = domainList.listIterator();
        while (iterator.hasNext()) {
            OfflineDevOperationTaskManageDomain next = iterator.next();
            next.setPublishStatus(0);
            if (Objects.nonNull(flow.getXxlJobId())) {
                next.setPublishStatus(1);
            }
            if (flow.getIsLocked().equals(0)) {
                next.setIsLocked(0);
            }
            next.setPid(nodeId);
            next.setCanReRun(1);
            if (next.getNodeType().equals(OfflineDevNodeTypeEnum.VNODE_START.getNodeType())) {
                next.setCanReRun(0);
            }
            next.setUserName(user.getUserName());
            next.setHaveChildren(0);
            if (nodeDetailByTaskFlowId != null) {
                next.setCycle(nodeDetailByTaskFlowId.getCycle());
            }
            next.setScheduleInfo(schedule);
            if (Objects.nonNull(nodeType) && !next.getNodeType().equals(nodeType)) {
                iterator.remove();
            }
        }

        List<OfflineDevOperationTaskManageDomain> res =
            domainList.stream()
                .sorted(Comparator.comparing(OfflineDevOperationTaskManageDomain::getNodeId))
                .collect(Collectors.toList());
        return res;
    }

    @Override
    public boolean setTaskFlowScheduleStatus(List<Long> flowIds, Integer isLocked, Long projectId) {
        flowIds.forEach(
            e -> {
                offlineDevMenuNodeService.setTaskFlowScheduleStatus(e, isLocked);
            });

        return true;
    }

    @Override
    public List<UserDomain> getUserList(Long projectId) {
        QueryWrapper<DatadevMenuNodeEntity> queryChangeReasonWrapper =
            new QueryWrapper<DatadevMenuNodeEntity>()
                .select("creator_id as creatorId")
                .eq("project_id", projectId)
                .groupBy("creator_id");
        List<Map<String, Object>> maps = listMaps(queryChangeReasonWrapper);
        List<String> userIds = Lists.newArrayList();
        maps.forEach(
            e -> {
                Object creatorId = e.get("creatorid");
                if (creatorId == null) {
                    creatorId = e.get("CREATORID");
                }
                if (creatorId == null) {
                    creatorId = e.get("creatorId");
                }
                userIds.add(creatorId.toString());
            });
        List<UserDomain> convert = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(userIds)) {
            convert = userService.getMapUserDomainByUserIds(userIds).values().stream().toList();
        }
        return convert;
    }

    @Override
    public void getTreeIds(Long projectId, List<Long> ids, Long pid) {

        LambdaQueryWrapper<DatadevMenuNodeEntity> eq =
            new LambdaQueryWrapper<DatadevMenuNodeEntity>()
                .eq(DatadevMenuNodeEntity::getProjectId, projectId)
                .eq(DatadevMenuNodeEntity::getPid, pid);
        List<DatadevMenuNodeEntity> list = this.list(eq);
        for (DatadevMenuNodeEntity datadevMenuNodeEntity : list) {
            Integer nodeType = datadevMenuNodeEntity.getNodeType();
            if (OfflineDevNodeTypeEnum.DIR_TASK_FLOW.getNodeType().equals(nodeType)) {
                ids.add(datadevMenuNodeEntity.getId());
            } else if (OfflineDevNodeTypeEnum.DIR_NORMAL.getNodeType().equals(nodeType)) {
                getTreeIds(projectId, ids, datadevMenuNodeEntity.getId());
            }
        }
        return;
    }
}
